import { Provider, useDispatch } from 'react-redux';
import {
  combineSlices,
  configureStore,
  createSlice,
  Reducer,
} from '@reduxjs/toolkit';
import { ReactNode, useCallback, useRef } from 'react';
import { myContext } from '@/core/injectContext.ts';

export const defaultSlice = createSlice({
  name: 'tem1p',
  initialState: {
    temp: 0,
  },
  reducers: {
    setTemp: (state, action) => {
      state.temp = action.payload;
    },
  },
});
// TODO перенести в отдельный файл, тк MyProvider должен находиться в src/core
export const rootReducer = combineSlices(defaultSlice);

export const store = configureStore({
  reducer: rootReducer,
  middleware: getDefaultMiddleware => getDefaultMiddleware(),
});

export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();

export const MyProvider = ({ children }: { children: ReactNode }) => {
  const counterRef = useRef<Record<string, number>>({});

  const updateRedux = useCallback(
    <T extends { name: string; reducer: Reducer<T> }>({ reducer, name }: T) => {
      if (counterRef.current[name]) {
        counterRef.current[name]++;
      } else {
        /** overrideExisting позволяет перезатирать текущий редюсер.
         * Нужен для кейса, когда мы заново инициализируем редюсер */
        rootReducer.inject(
          { reducer, reducerPath: name },
          { overrideExisting: true },
        );
        /** Подстраховка, которая гарантирует, что при инициализации
         * стор будет соответствовать initialState*/
        store.dispatch({ type: name + '/_init' });

        counterRef.current[name] = 1;
      }
    },
    [],
  );

  const removeSlice = useCallback(
    <T extends { name: string; getInitialState: () => unknown }>({
      name,
      getInitialState,
    }: T) => {
      try {
        counterRef.current[name]--;

        if (!counterRef.current[name]) {
          /** overrideExisting позволяет перезатирать текущий редюсер.
           * Это гарантирует что новый выброс эшена этого редюсера не изменит состояние  */
          rootReducer.inject(
            { reducer: getInitialState, reducerPath: name },
            { overrideExisting: true },
          );
          store.dispatch({ type: name + '/_destroy' });
        }
      } catch (e) {
        console.error(
          'Счетчик уже был undef или ошибка при замене редюсера на заглушку',
          e,
        );
      }
    },
    [],
  );

  if (process.env.__DEBUG__) {
    window.store = store;
  }

  return (
    <Provider store={store}>
      <myContext.Provider value={{ updateRedux, removeSlice }}>
        {children}
      </myContext.Provider>
    </Provider>
  );
};
